"""
Classes for managed lists and tree lists

Managed lists are extensions of wx.ListCtrls that allow for
saving column settings such as width and alternate titles
"""
import sys
import wx
import wx.gizmos as gizmos
from wx.lib import masked
from wx.lib.mixins.listctrl import ColumnSorterMixin
from operator import itemgetter

from menu import Menu
from LMG.Utility.helpers import makeNumCtrl, getFontFromInfo, forceunicode
from LMG.GUI.Base.ArtManager import ArtManager


################################################################

class ColumnsDialog(wx.Dialog):
    """
    Allows a user to alter the settings and appearance of 
    columns in a ManagedList.
    """
    def __init__(self, list):
                
        title = _('Columns')
        
        wx.Dialog.__init__(self, list, wx.ID_ANY, title)

        outerbox = wx.BoxSizer(wx.VERTICAL)
        
        self.columnsPanel = ColumnsPanel(self, list)
        
        okbtn  = wx.Button(self, wx.ID_OK, _('OK'))
        okbtn.SetDefault()
        self.Bind(wx.EVT_BUTTON, self._onOK, okbtn)
        
        cancelbtn = wx.Button(self, wx.ID_CANCEL, _('Cancel'))

        applybtn  = wx.Button(self, wx.ID_APPLY, _('Apply'))
        self.Bind(wx.EVT_BUTTON, self._onApply, applybtn)
                
        buttonbox = wx.BoxSizer(wx.HORIZONTAL)
        buttonbox.Add(okbtn, 0, wx.EXPAND|wx.ALL, 5)
        buttonbox.Add(applybtn, 0, wx.EXPAND|wx.ALL, 5)
        buttonbox.Add(cancelbtn, 0, wx.EXPAND|wx.ALL, 5)
        
        outerbox.Add(self.columnsPanel, 0, wx.EXPAND|wx.ALL, 5)
        outerbox.Add(buttonbox, 0, wx.CENTER)
       
        self.SetAutoLayout(True)
        self.SetSizerAndFit(outerbox)
        
    def _onOK(self, event = None):
        if self._onApply(event):
            self.EndModal(wx.ID_OK)
        
    def _onApply(self, event = None):
        self.columnsPanel.apply()
        return True


################################################################

class ColumnsPanel(wx.Panel):
    """
    Contains the interface elements for a ColumnsDialog
    """
    def __init__(self, parent, list):
        wx.Panel.__init__(self, parent, wx.ID_ANY)
        
        self.list = list
        self.columns = list.columns
        
        self.changed = False
        
        self.changingvalue = False

        self.columnlist = []

        self.leftindex = -1
        self.rightindex = -1
        
        if hasattr(list, "maincolumn"):
            self.maincolumn = list.maincolumn
        else:
            self.maincolumn = -1
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        listsizer = wx.BoxSizer(wx.HORIZONTAL)
        
        # unselected list ctrl
        self.checklistbox = wx.CheckListBox(self, wx.ID_ANY, style = wx.LB_SINGLE, size = wx.Size(150, 200))

        listsizer.Add(self.checklistbox, 0, wx.ALL, 5)

        # Up & Down button
        ###################

        self.upbutton = utility.makeBitmapButton(self, wx.ArtProvider.GetBitmap(wx.ART_GO_UP, wx.ART_TOOLBAR), _('Move Up'), self._OnMove)
        self.downbutton = utility.makeBitmapButton(self, wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR), _('Move Down'), self._OnMove)

        updownsizer = wx.BoxSizer(wx.VERTICAL)
        
        updownsizer.Add(self.upbutton, 0, wx.BOTTOM, 5)
        updownsizer.Add(self.downbutton, 0, wx.TOP, 5)
        
        listsizer.Add(updownsizer, 0, wx.ALL, 5)
        
        sizer.Add(listsizer, 0)
        
        labelSizer = wx.FlexGridSizer(cols = 2, vgap = 5, hgap = 10)
        
        labelSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Default name:')), 0, wx.ALIGN_CENTER_VERTICAL)
        self.labeldefault = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_READONLY)
        labelSizer.Add(self.labeldefault, 0, wx.ALIGN_CENTER_VERTICAL)        
         
        labelSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Display name:')), 0, wx.ALIGN_CENTER_VERTICAL)
        self.labelsetting = wx.TextCtrl(self, wx.ID_ANY, "")
        labelSizer.Add(self.labelsetting, 0, wx.ALIGN_CENTER_VERTICAL)
       
        labelSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Column width:')), 0, wx.ALIGN_CENTER_VERTICAL)
        initialvalue = 0
        self.widthsetting = makeNumCtrl(self, initialvalue, max = 2000)
        labelSizer.Add(self.widthsetting, 0, wx.ALIGN_CENTER_VERTICAL)
        
        sizer.Add(labelSizer, 0, wx.ALL, 5)
               
        self.getDefaultValues()

        self.labeldefault.Enable(False)
        self.labelsetting.Enable(False)
        self.widthsetting.Enable(False)
        
        self.SetSizerAndFit(sizer)

        # Add Event
        #########################
        self.Bind(wx.EVT_LISTBOX, self.OnSelect, self.checklistbox)
        self.Bind(wx.EVT_TEXT, self.OnChangeLabel, self.labelsetting)
        self.Bind(masked.EVT_NUM, self.OnChangeWidth, self.widthsetting)

    def OnCheckItem(self, index, flag):
        pass
    
    def getDefaultValues(self):
        unselected = []
        
        selected = []

        for colid in range(self.columns.minid, self.columns.maxid):
            item = {}            

            item["colid"] = colid
            item["rank"] = utility.config.Read(self.list.prefix + str(colid) + "_rank", "int")
            item["text"] = utility.lang.get(self.list.prefix + str(colid) + "_text")
            item["default_text"] = utility.lang.getDefault(self.list.prefix + str(colid) + "_text")
            item["width"] = utility.config.Read(self.list.prefix + str(colid) + "_width", "int")

            if item["rank"] >= 0:
                selected.append(item)
            elif self.maincolumn and self.maincolumn == colid:
                item["rank"] = 0
                selected.append(item)
            else:
                unselected.append(item)
        
        unselected.sort(key=itemgetter('rank'))
        selected.sort(key=itemgetter('rank'))
        
        self.columnlist = selected + unselected
        
        self.checklistbox.Set([item["text"] for item in self.columnlist])
        
        for i in range(len(self.columnlist)):
            if self.columnlist[i]["rank"] != -1:
                self.checklistbox.Check(i)
    
    def OnSelect(self, event):
        """
        Select one of the items in the list
        """
        # The index of the selection within the checklistbox
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            self.labeldefault.Enable(False)
            self.labelsetting.Enable(False)
            self.widthsetting.Enable(False)
            return
        self.labeldefault.Enable(True)
        self.labelsetting.Enable(True)
        self.widthsetting.Enable(True)
            
        self.changingvalue = True
        self.labeldefault.SetValue(self.columnlist[index]["default_text"])
        self.labelsetting.SetValue(self.columnlist[index]["text"])
        self.widthsetting.SetValue(self.columnlist[index]["width"])
        self.changingvalue = False
        
    def OnChangeLabel(self, event):
        if self.changingvalue:
            return
        
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            return
        
        oldlabel = self.columnlist[index]["text"]
        newlabel = self.labelsetting.GetValue()
        try:
            if oldlabel == newlabel:
                return
        except:
            pass
            
        self.columnlist[index]["text"] = newlabel
        self.checklistbox.SetString(index, newlabel)
        
    def OnChangeWidth(self, event):
        if self.changingvalue:
            return
        
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            return
        
        self.columnlist[index]["width"] = self.widthsetting.GetValue()
    
    def _OnMove(self, event):
        """
        Move a list item up or down
        """
        # Move up
        if event.GetId() == self.upbutton.GetId():
            direction = -1
        # Move down
        else:
            direction = 1
        
        index = self.checklistbox.GetSelection()
        if index == wx.NOT_FOUND:
            # Nothing is selected:
            return
        
        # TODO
        if self.columnlist[index]["rank"] == self.maincolumn:
            return


        if (direction == 1) and (index == self.checklistbox.GetCount() - 1):
            #Last Item can't move down anymore
            return
#        elif (direction == -1) and (index == 0):
        elif (direction == -1) and (index <= 1):
            # First Item can't move up anymore
            return
        else:
            self.columnlist[index], self.columnlist[index + direction] = self.columnlist[index + direction], self.columnlist[index]

            col1text = self.checklistbox.GetString(index)
            col2text = self.checklistbox.GetString(index + direction)

            col1checked = self.checklistbox.IsChecked(index)
            col2checked = self.checklistbox.IsChecked(index + direction)

            #Update display
            self.checklistbox.SetString(index + direction, col1text)
            self.checklistbox.SetString(index, col2text)
            
            self.checklistbox.Check(index + direction, col1checked)
            self.checklistbox.Check(index, col2checked)
            
            self.checklistbox.SetSelection(index + direction)

    def apply(self):
        selected = 0
        for i in range(0, self.checklistbox.GetCount()):
            if self.checklistbox.IsChecked(i):
                self.columnlist[i]["rank"] = selected
                selected += 1
            else:
                self.columnlist[i]["rank"] = -1

        # Check to see if anything has changed
        configchange = False
        langchange = False
        for item in self.columnlist:
            prefix = self.list.prefix + str(item["colid"])
            
            if utility.config.Write(prefix + "_rank", item["rank"]):
                configchange = True

            if utility.config.Write(prefix + "_width", item["width"]):
                configchange = True
            
            if utility.lang.writeUser(prefix + "_text", item["text"]):
                langchange = True

        # APPLY on-the-fly
        if configchange or langchange:
            if configchange:
                utility.config.Flush()
            if langchange:
                utility.lang.flush()
                if hasattr(self.list, "columnpopup"):
                    del self.list.columnpopup
            self.list.updateColumns()


##############################################################

class ColumnManager:
    """
    Keep track of the settings for order and appearance of
    columns in a ManagedList
    """
    def __init__(self, list):        
        self.minid = list.minid
        self.maxid = list.maxid
        self.prefix = list.prefix
        
        if hasattr(list, "maincolumn"):
            self.maincolumn = list.maincolumn
        else:
            self.maincolumn = -1

        self.active = []
        
        self.getColumnData()
       
    def getNumCol(self):
        return len(self.active)
        
    def getRankfromID(self, colid):
        return utility.config.Read(self.prefix + str(colid) + "_rank", "int")

    def getIDfromRank(self, rankid):
        return self.active[rankid][0]
    
    def getTextfromRank(self, rankid):
        colid = self.active[rankid][0]
        return utility.lang.get(self.prefix + str(colid) + "_text")
    
    def getTextfromID(self, colid):
        return utility.lang.get(self.prefix + str(colid) + "_text")
    
    def getValuefromRank(self, rankid):
        colid = self.active[rankid][0]
        return utility.config.Read(self.prefix + str(colid) + "_width", "int")

    def getColumnData(self):
        self.active = []

        # Get the list of active columns
        for colid in range(self.minid, self.maxid):
            rank = utility.config.Read(self.prefix + str(colid) + "_rank", "int")
            
            if (rank >= 0):
                self.active.append([colid, rank])
            elif self.maincolumn != -1 and colid == self.maincolumn:
                self.active.append([colid, 0])
            else:
                utility.config.Write(self.prefix + str(colid) + "_rank", -1)
                
        # Sort the columns by rank
        self.active.sort(key = lambda x:x[1])
        
        # Make sure that the columns are in an order that makes sense
        # (i.e.: if we have a config with IDs of 4, 99, 2000 then
        #        we'll convert that to 0, 1, 2)
        for i in range(len(self.active)):
            colid = self.active[i][0]
            rank = i
            self.active[i][1] = rank
            utility.config.Write(self.prefix + str(colid) + "_rank", rank)
        utility.config.Flush()

################################################################

class ManagedList(wx.ListCtrl, ColumnSorterMixin):
    """
    An extension of wx.ListCtrl that keeps track of column
    settings in a unified manner.
    """
    def __init__(self, parent, style, prefix, minid, maxid, rightalign = [], centeralign = []):        
        if wx.Platform == "__WXMAC__":
            style &= ~wx.LC_VRULES
        wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style = style)
        
        self.parent = parent
        
        self.prefix = prefix
        self.minid = minid
        self.maxid = maxid

        # TODO: main column
        self.maincolumn = minid
        
        self.rightalign = rightalign
        self.centeralign = centeralign
                
        self.columns = ColumnManager(self)
        
        self.Bind(wx.EVT_LIST_COL_END_DRAG, self.OnResizeColumn)
        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColRightClick)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColLeftClick)

        self.loadTranslation()
        
        self.loadColumns()
        
        self.loadFont()
        
        # Add to the list of ManagedList objects
        utility.lists[self] = True
        
        self.lastcolumnselected = -1

        # Add Bitmaps for UP\DOWN sorting
        self.il = wx.ImageList(16, 16)
        self.il.Add(self.createBlank())         # 0
        self.il.Add(self.createArrow('up'))     # 1
        self.il.Add(self.createArrow('down'))   # 2
        
        ColumnSorterMixin.__init__(self, int(self.maxid - self.minid))

    def EnsureSelectedVisible(self):
        selected = self.GetFirstSelected()
        if selected > -1:
            self.EnsureVisible(selected)
        
    def loadTranslation(self, update = False):
        pass
    
    def createBlank(self):
        bmp = wx.EmptyBitmap(16, 16)
        bmp.SetMask(wx.Mask(bmp, (0, 0, 0)))
        return bmp
        
    def createArrow(self, dir):
        # Initialize DC
        dc = wx.MemoryDC()
        dc.SetBackgroundMode(wx.TRANSPARENT)
        dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DSHADOW)))

        # Set Bitmap
        bmp = wx.EmptyBitmap(16, 16)
        dc.SelectObject(bmp)

        # Draw Arrow
        dc.Clear()
        for i in xrange(5):
            if dir == "up":
                dc.DrawLine(i, 9-i, 9-i, 9-i)
            else:
                dc.DrawLine(i, i+5, 9-i, i+5)

        # Set Mask
        del dc
        bmp.SetMask(wx.Mask(bmp, (255, 255, 255)))

        return bmp
            
    def SortItems(self,sorter=None):
        pass
    
    def OnColLeftClick(self, event):
        event.Skip()
        
    def GetListCtrl(self):
        return self

    def GetSortImages(self):
        if wx.Platform == "__WXMAC__":
            return (0, 0)
        return (1, 2)
        
    def loadFont(self):
        """
        Get the font settings for the list, then apply them.
        """
        # Get font information
        fontinfo = utility.config.Read('listfont', "bencode-fontinfo")
        font = getFontFromInfo(fontinfo)

        # Only change if we've gotten an acceptable font
        if font.Ok():
            if self.GetItemCount() > 0:
                # Jump to the top of the list
                # (make sure that there are items in the list, though)
                try:
                    self.EnsureVisible(0)
                except:
                    pass
            # Change the font
            self.SetFont(font)
            # Refresh to make the change visable
            self.Refresh()

    def loadColumns(self):
        numcol = self.GetColumnCount()
        if numcol:
            toadd = range(1, self.columns.getNumCol())
            for i in xrange(1, numcol):
                self.DeleteColumn(1)
        else:
            toadd = range(self.columns.getNumCol())

        # Read status display
        ####################################
        for rank in toadd:
            colid = self.columns.getIDfromRank(rank)
            if colid in self.rightalign:
                style = wx.LIST_FORMAT_RIGHT
            elif colid in self.centeralign:
                style = wx.LIST_FORMAT_CENTER
            else:
                style = wx.LIST_FORMAT_LEFT
            text = self.columns.getTextfromRank(rank)
            width = self.columns.getValuefromRank(rank)
            # Don't allow a column to have a size of 0
            if width == 0:
                width = -1
            self.InsertColumn(rank, text, style, width)
        
    def OnResizeColumn(self, event):
        """
        Save the width of the column that was just resized
        """
        rank = event.GetColumn()
        width = self.GetColumnWidth(rank)
        colid = self.columns.getIDfromRank(rank)
        utility.config.Write(self.prefix + str(colid) + "_width", width)
        utility.config.Flush()
            
    def OnColRightClick(self, event):
        """
        Create a list of columns that are active/inactive
        """
        if not hasattr(self, "columnpopup"):
            self.makeColumnPopup()
            
        # Check off columns for all that are currently active
        for colid in range(self.minid, self.maxid):            
            if utility.config.Read(self.prefix + str(colid) + "_rank", "int") > -1:
                self.columnpopup.Check(777 + colid, True)
            else:
                self.columnpopup.Check(777 + colid, False)

        self.lastcolumnselected = event.GetColumn()
        if wx.Platform == "__WXMSW__":  # wxPython bug fix
            c = self.getClickedColumn(event.GetPoint()[0])
            if c >= 0:
                self.lastcolumnselected = c
        self.PopupMenu(self.columnpopup, event.GetPosition())
        
    def getClickedColumn(self, x):
        """ Returns the column number of the list from the x coordinate of a point inside the list """
        currentwidth = -self.GetScrollPos(wx.HORIZONTAL)
        for rank in xrange(self.GetColumnCount()):
            currentwidth += self.GetColumnWidth(rank)
            if x < currentwidth:
                return rank
        return -1
        
    def makeColumnPopup(self):
        self.columnpopup = Menu()
        
        for colid in range(self.minid, self.maxid):            
            text = utility.lang.get(self.prefix + str(colid) + '_text')
            self.columnpopup.Append(777 + colid, text, text, wx.ITEM_CHECK)
            
        self.columnpopup.AppendSeparator()
        
        utility.makePopup(self.columnpopup, self.onColumnDialog, _('more...'))

        startid = 777 + self.minid
        endid = 777 + (self.maxid - 1)

        self.Bind(wx.EVT_MENU, self.onSelectColumn, id=startid, id2=endid)
        
    def onColumnDialog(self, event = None):
        dialog = ColumnsDialog(self)
        dialog.ShowModal()
        dialog.Destroy()

    def onSelectColumn(self, event):
        eventid = event.GetId()        
        colid = eventid - 777
        oldrank = utility.config.Read(self.prefix + str(colid) + "_rank", "int")

        if oldrank > -1:
            # Column was deselected, don't show it now
            # (update ranks for the rest of the columns that appeared after it)
            for i in range (self.minid, self.maxid):
                temprank = utility.config.Read(self.prefix + str(i) + "_rank", "int")
                if (i == colid):
                    writeval = -1
                elif (temprank >= oldrank):
                    writeval = temprank - 1
                else:
                    writeval = temprank
                utility.config.Write(self.prefix + str(i) + "_rank", writeval)
        else:
            # Column was selected, need to show it
            # Put it after the closest column
            if self.lastcolumnselected >= 0:
                newrank = self.lastcolumnselected + 1
            # (just tack it on the end of the display)
            else:
                newrank = self.GetColumnCount()
            
            for i in range (self.minid, self.maxid):
                temprank = utility.config.Read(self.prefix + str(i) + "_rank", "int")
                if (i == colid):
                    writeval = newrank
                elif (temprank >= newrank):
                    writeval = temprank + 1
                else:
                    writeval = temprank
                utility.config.Write(self.prefix + str(i) + "_rank", writeval)

        utility.config.Flush()
        
        # Write changes to the config file and refresh the display
        self.updateColumns()
    
    def updateColumns(self):
        self.columns.getColumnData()
        self.loadColumns()
        
        self.onUpdateColumns(force = True)
        ArtManager.Get().MakeAlternateList(self)
        
    def getSelected(self, firstitemonly = False, reverse = False):
        selected = []
        index = self.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
        while index != -1:
            selected.append(index)
            if (firstitemonly):
                return selected
            index = self.GetNextItem(index, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
        selected.sort()
        if reverse:
            selected.reverse()
        return selected
        
    def selectAll(self):
        for index in range(self.GetItemCount()):
            self.Select(index)
            
    def unSelectAll(self):
        for index in range(self.GetItemCount()):
            self.Select(index, 0)
    
    def invertSelection(self):
        for index in range(self.GetItemCount()):
            self.SetItemState(index, 4 - self.GetItemState(index, wx.LIST_STATE_SELECTED), wx.LIST_STATE_SELECTED)

    def setString(self, index, rank, text, force = False):
        if type(text) is str:
            text = forceunicode(text)

        if not force:
            # Only update if the text has changed
            try:
                oldtext = self.GetItem(index, rank).GetText()
                if text != oldtext:
                    force = True
            except:
                force = True
        
        if force:
            self.SetStringItem(index, rank, text)
            
    def setNumberOfItems(self, newlen):
        changed = False
        
        while self.GetItemCount() < newlen:
            self.InsertStringItem(self.GetItemCount(), "")
            changed = True
        while self.GetItemCount() > newlen:
            self.DeleteItem(0)
            changed = True
            
        if changed:
            self.unSelectAll()

################################################################

class ManagedTreeList(gizmos.TreeListCtrl):
    """
    An extension of wx.gizmos.TreeListCtrl that keeps track of column
    settings in a unified manner.
    """
    def __init__(self, parent, style, prefix, minid, maxid, maincolumn, rightalign = [], centeralign = []):
        gizmos.TreeListCtrl.__init__(self, parent, wx.ID_ANY, style = style)
        
        self.parent = parent
        
        self.prefix = prefix
        self.minid = minid
        self.maxid = maxid
        self.maincolumn = maincolumn
        
        self.rightalign = rightalign
        self.centeralign = centeralign
                
        self.columns = ColumnManager(self)
        
        self.Bind(wx.EVT_LIST_COL_END_DRAG, self.OnResizeColumn)
        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColRightClick)
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.onTreeItemRightClick)

        self.loadTranslation()
    
        self.loadColumns()
        
        self.loadFont()
        
        # Add to the list of ManagedList objects
        utility.lists[self] = True
        
        self.lastcolumnselected = -1
        
    def loadTranslation(self, update = False):
        pass

    def onTreeItemRightClick(self, event):
        """
        When right-clicking on a tree item,
        select that item if it isn't already selected.
        """
        treeid = event.GetItem()
        if not self.IsSelected(treeid):
            self.SelectItem(treeid)
        
    def loadFont(self):
        """
        Get the font settings for the list, then apply them.
        """
        # Get font information
        fontinfo = utility.config.Read('listfont', "bencode-fontinfo")
        font = getFontFromInfo(fontinfo)

        # Only change if we've gotten an acceptable font
        if font.Ok():
            root = self.GetRootItem()
            if root:
                # Jump to the top of the list
                # (make sure that there are items in the list, though)
                self.EnsureVisible(root)
            # Change the font
            self.SetFont(font)
            # Refresh to make the change visable
            self.Refresh()

    def loadColumns(self):
        # Delete Old Columns (if they exist)
        #################################################
        numcol = self.GetColumnCount()
        for i in range(numcol):
            self.RemoveColumn(0)

        # Read status display
        ####################################
        
        for rank in range(self.columns.getNumCol()):
            colid = self.columns.getIDfromRank(rank)
            if colid in self.rightalign:
                style = gizmos.TL_ALIGN_RIGHT
            elif colid in self.centeralign:
                style = gizmos.TL_ALIGN_CENTER
            else:
                style = gizmos.TL_ALIGN_LEFT
            text = self.columns.getTextfromRank(rank)
            width = self.columns.getValuefromRank(rank)
            # Don't allow a column to have a size of 0
            if width == 0:
                width = -1
            self.AddColumn(text)
            self.SetColumnWidth(rank, width)
            self.SetColumnAlignment(rank, style)
            
            if colid == self.maincolumn:
                self.SetMainColumn(rank)
            
        
    def OnResizeColumn(self, event):
        """
        Save the width of the column that was just resized
        """
        rank = event.GetColumn()
        width = self.GetColumnWidth(rank)
        colid = self.columns.getIDfromRank(rank)
        utility.config.Write(self.prefix + str(colid) + "_width", width)
        utility.config.Flush()
            
    def OnColRightClick(self, event):
        """
        Create a list of columns that are active/inactive
        """
        if not hasattr(self, "columnpopup"):
            self.makeColumnPopup()
            
        # Check off columns for all that are currently active
        for colid in range(self.minid, self.maxid):            
            if utility.config.Read(self.prefix + str(colid) + "_rank", "int") > -1:
                self.columnpopup.Check(777 + colid, True)
            else:
                self.columnpopup.Check(777 + colid, False)
        
        self.lastcolumnselected = event.GetColumn()
               
        self.PopupMenu(self.columnpopup, event.GetPosition())
        
    def makeColumnPopup(self):
        self.columnpopup = Menu()        
        for colid in range(self.minid, self.maxid):            
            text = utility.lang.get(self.prefix + str(colid) + '_text')
            self.columnpopup.Append(777 + colid, text, text, wx.ITEM_CHECK)
            
        self.columnpopup.AppendSeparator()
        
        utility.makePopup(self.columnpopup, self.onColumnDialog, _('more...'))

        startid = 777 + self.minid
        endid = 777 + (self.maxid - 1)

        self.Bind(wx.EVT_MENU, self.onSelectColumn, id=startid, id2=endid)
        
    def onColumnDialog(self, event = None):
        dialog = ColumnsDialog(self)
        dialog.ShowModal()
        dialog.Destroy()

    def onSelectColumn(self, event):
        eventid = event.GetId()        
        colid = eventid - 777
        oldrank = utility.config.Read(self.prefix + str(colid) + "_rank", "int")

        if oldrank > -1:         
            # If the column is the maincolumn, ignore the request to hide it
            if colid == self.maincolumn:
                return
               
            # Column was deselected, don't show it now
            # (update ranks for the rest of the columns that appeared after it)
            for i in range (self.minid, self.maxid):
                temprank = utility.config.Read(self.prefix + str(i) + "_rank", "int")
                if (i == colid):
                    writeval = -1
                elif (temprank >= oldrank):
                    writeval = temprank - 1
                else:
                    writeval = temprank
                utility.config.Write(self.prefix + str(i) + "_rank", writeval)
        else:
            # Column was selected, need to show it
            # Put it after the closest column
            if self.lastcolumnselected >= 0:
                newrank = self.lastcolumnselected + 1
            # (just tack it on the end of the display)
            else:
                newrank = self.GetColumnCount()
            
            for i in range (self.minid, self.maxid):
                temprank = utility.config.Read(self.prefix + str(i) + "_rank", "int")
                if (i == colid):
                    writeval = newrank
                elif (temprank >= newrank):
                    writeval = temprank + 1
                else:
                    writeval = temprank
                utility.config.Write(self.prefix + str(i) + "_rank", writeval)

        utility.config.Flush()
        
        # Write changes to the config file and refresh the display
        self.updateColumns()
    
    def updateColumns(self):
        self.columns.getColumnData()
        self.loadColumns()
        
        self.parent.loadValues()

    def onUpdateColumns(self, force = False):
        raise NotImplementedError
    
    def getItems(self, root = None, getChildren = True, expandedOnly = False):
        if root is None:
            root = self.GetRootItem()
        
        # Use a dict to ensure that we don't
        # have an item present more than once
        itemlist = {}
        itemlist[root] = 1
        
        treeid, cookie = self.GetFirstChild(root)
        
        while treeid:
            if getChildren and \
               self.ItemHasChildren(treeid) and \
               ((not expandedOnly) or self.IsExpanded(treeid)):
                sublist = self.getItems(treeid, expandedOnly = expandedOnly)
                for item in sublist:
                    itemlist[item] = 1
            else:
                itemlist[treeid] = 1
            treeid, cookie = self.GetNextChild(root, cookie)
            
        return itemlist.keys()

    def getSelectedItems(self, root = None):
        if root is None:
            root = self.GetRootItem()
        
        # Use a dict to ensure that we don't
        # have an item present more than once
        itemlist = {}
        if self.IsSelected(root):
            itemlist[root] = 1
        
        treeid, cookie = self.GetFirstChild(root)
        
        while treeid:
            if self.ItemHasChildren(treeid):
                # Item has children
                sublist = []
                if self.IsExpanded(treeid):
                    # Item is expanded
                    # Get selected items beneath it
                    sublist = self.getSelectedItems(treeid)
                #elif self.IsSelected(treeid):
                if self.IsSelected(treeid):
                    # Item is not expanded, but is selected
                    # Get all items beneath it
                    sublist = self.getItems(treeid)
                for item in sublist:
                    itemlist[item] = 1
            elif self.IsSelected(treeid):
                # Item doesn't have children, but is selected
                itemlist[treeid] = 1
            
            treeid, cookie = self.GetNextChild(root, cookie)
            
        return itemlist.keys()
   
    def getSelected(self, firstitemonly = False):
        if firstitemonly:
            return [self.GetSelection()]

        selected = self.getSelectedItems()
            
        return selected
        
    def selectAll(self):
        self.SelectAll()

    def invertSelection(self):
        items = self.getItems(expandedOnly = True)
        for treeid in items:
            self.SelectItem(treeid, False)

    def setString(self, treeid, rank, text, force = False):
        if type(text) is str:
            text = forceunicode(text)

        if not force:
            # Only update if the text has changed
            try:
                oldtext = self.GetItemText(treeid, rank)
                if text != oldtext:
                    force = True
            except:
                force = True
        
        if force:
            self.SetItemText(treeid, text, rank)